package ${techspec.packageName}.pages.generated;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIData;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import javax.inject.Inject;
import javax.inject.Named;
import javax.enterprise.inject.New;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import ${techspec.packageName}.dao.*;
import ${techspec.packageName}.entity.*;
import ${techspec.packageName}.service.*;

import ${techspec.packageName}.pages.generated.Generated${page.x.javaName};

/**
 * This object represents the ${page.name} page in the application.
 * 
 *  Not sure if this object is really needed or not, but creating it for now 
 *  as a holder of user facing input/output objects.
 *  
 *  This object should also vastly decouple the view from the business logic
 *  
 */
@Named
public abstract class Generated${page.x.javaName} implements java.io.Serializable {
    private final Log log = LogFactory.getLog(getClass());

## // unique entities on this controller
#foreach ($entity in $page.x.uniqueEntities)
        @Inject
        protected ${entity.name}Dao ${entity.id}Dao;

        @Inject 
        protected ${entity.name}Service ${entity.id}Service;
#end   

    @PostConstruct
    public void init() {
## entities updates by this page
        // init entities if present
#foreach ($entity in $page.inputEntities)
        this.${entity.id} = new ${entity.name}();
#end

        // init options
#foreach ($form in $page.forms)
#foreach($input in $form.dropdownOrRadioInputs)
        init${input.x.javaName}();
#end## // init each radio/dropdown
#end## // init each dropdown radio  
    }

## entities updates by this page
##foreach ($entity in $page.inputEntities)
##@Inject @New
##getterSetter({"type" : "${entity.name}", "id" : "${entity.id}", "name" : "${entity.name}", "title" : "${entity.title}", "description" : "${entity.description} - ${pageEntity.type}", "fieldVisibility" : "protected"})
##end

## // forms
#foreach ($form in $page.forms)
## // actions
#foreach ($action in $form.actions)
    public String ${action.x.javaId}() {
        log.info("${action.x.javaId} called...");

        return ${action.x.javaId}${action.defaultOutcome.name}();
    }

#foreach ($outcome in $action.outcomes)
    public String ${action.x.javaId}${outcome.name}() {
#if ($outcome.internal)
        FacesMessage message = new FacesMessage();
        message.setSummary("${outcome.message}");
        message.setDetail("${outcome.message}");
        message.setSeverity(#if($outcome.negative)FacesMessage.SEVERITY_ERROR#{else}FacesMessage.SEVERITY_INFO#end);
        FacesContext context = FacesContext.getCurrentInstance();
        context.addMessage(null, message);

        return "${outcome.page.id}";
#else
        // return a redirect to an external page '${outcome.href}'
        // TODO: implement me
        return null;
#end
    }
#end## // foreach outcomes
#end##// foreach action

## // foreach input
#foreach ($input in $form.inputs)
    public void clear${input.x.javaName}() {
#if ($input.type == 'upload')
    set${input.x.javaName}ContentType(null);
    set${input.x.javaName}InputStream(null);
    set${input.x.javaName}FileName(null);
#else
        set${input.x.javaName}(null);
#end
    }

#getterSetter({"type" : "${input.x.javaType}", "id" : "${input.x.javaId}", "name" : "${input.x.javaName}", "title" : "${input.title}", "description" : "${input.description}", "fieldVisibility" : "protected"})

## // validator for each input
    public void ${input.x.javaId}Validator(javax.faces.context.FacesContext facesContext,
            javax.faces.component.UIComponent component, java.lang.Object value) {
        String valueStr = value.toString();

#if ($input.matchesInputValidation)        
        boolean valid = StringUtils.equals(get${input.matchesInputValidation.otherInput.x.javaName}(), valueStr);

        if (!valid) {
            FacesMessage message = new FacesMessage();
            message.setDetail("${input.matchesInputValidation.authoredOrGeneratedDescription}");
            message.setSummary("${input.matchesInputValidation.authoredOrGeneratedDescription}");
            message.setSeverity(FacesMessage.SEVERITY_ERROR);
            // TODO: cross field validation only works with immediate field updates, e.g. with ajax
            // throw new javax.faces.validator.ValidatorException(message);
        }
#end##if
    }

#end## // foreach input

## // foreach radio or dropdown inputs
#foreach($input in $form.dropdownOrRadioInputs)
#getterSetter({"type" : "List<SelectItem>", "id" : "${input.x.javaId}Options", "name" : "${input.x.javaName}Options", "title" : "${input.title} Options", "description" : "Options for ${input.title}", "fieldVisibility" : "protected"})

    public void init${input.x.javaName}() {
        this.${input.x.javaId}Options = new ArrayList<SelectItem>();
#foreach($allowedValueKey in $input.allowedValues.keySet())
        this.${input.x.javaId}Options.add(new SelectItem("${allowedValueKey}", "$input.allowedValues.get(${allowedValueKey})"));
#end## // foreach allowed value for radio/dropdown from appspec
    }
#end## // foreach radio/dropdown

    /**
     * a convenient method to clear this form
     */
    protected void clear${form.name}() {
#foreach ($input in ${form.inputs})
        clear${input.x.javaName}();
#end
    }
#end##// foreach form

## // foreach output list
#foreach ($outputList in $page.outputLists)
    /** the ${outputList.entity.name} row which on which the action was performed is stored on 
     * this field via the corresponding action listener if a user performs an action on the 
     * displayed ${outputList.entity.name} list 
     */
#getterSetter({"type" : "$outputList.entity.name", "id" : "${outputList.id}Selection", "name" : "${outputList.name}Selection", "title" : "Selected $outputList.name", "description" : "Selected $outputList.name row"})

    protected void init${outputList.name}() {
        // if ${outputList.id} has not been initialized, initialize it now
        if (get${outputList.name}() == null) {
            set${outputList.name}(load${outputList.name}());
        }
    }

    /**
     * clears the current value and loads the value again
     */
    protected void reload${outputList.name}() {
        clear${outputList.name}();
        load${outputList.name}();
    }

    /**
     * clears the current value of ${outputList.title} 
     */
    protected void clear${outputList.name}() {
        set${outputList.name}(null);
    }

    protected abstract List<${techspec.packageName}.entity.${outputList.entity.name}> load${outputList.name}();

    public void ${outputList.id}Listener(ActionEvent actionEvent) {
        UIComponent tmpComponent = actionEvent.getComponent();
    
        while (null != tmpComponent && !(tmpComponent instanceof UIData)) {
            tmpComponent = tmpComponent.getParent();
        }
    
        UIData uiData = (UIData) tmpComponent;
        Object selectedRow = uiData.getRowData();
        if (selectedRow instanceof ${outputList.entity.name}) {
            this.${outputList.id}Selection = (${outputList.entity.name}) selectedRow;
        }
    }

#getterSetter({"type" : "List<$outputList.entity.name>", "id" : "$outputList.id", "name" : "$outputList.name", "title" : "$outputList.title", "description" : "$outputList.description"})

#end

    /**
     * This method should be called before the page is loaded
     * 
     * All the page parameters should be consumed in this method
     */    
    public String ${page.x.pageLoadActionId}() {
    ## // from params
    #foreach($pageParameter in $page.parameters) 
    #if (${pageParameter.entityProperty} && ${pageParameter.entityProperty.primaryKey})
        init${pageParameter.entityProperty.entity.name}();
    #end
    #end
    
    ## // from outputLists
    #foreach($outputList in $page.outputLists)## for each entity list, populate from the dao
        init${outputList.name}();
    
    #end 
    
    ## // from outputs
    #foreach($output in $page.outputs)## for each output, populate from the dao
    #if (${context.get($output).generateEntityFieldInController})
        init${output.name}();
    #end
    #end
          // if you need to add more code, override this method in the child class
    
        return null;
    }
    
#foreach ($pageEntity in ${page.x.entities})
#set ($entity = $pageEntity.entity)
@Inject @New
#getterSetter({"type" : "${entity.name}", "id" : "${pageEntity.id}", "name" : "${pageEntity.name}", "title" : "${entity.title}", "description" : "${entity.description} - ${pageEntity.type}", "fieldVisibility" : "protected"})

    protected void init${pageEntity.name}() {
        // if ${outputList.id} has not been initialized, initialize it now
        if (get${pageEntity.name}() == null) {
            set${pageEntity.name}(load${pageEntity.name}());
        }
        
#if ($page.entitiesUpdated.get($pageEntity.entity))
        // updating all linked entities here
#set ($entityForm = $page.entitiesUpdated.get($pageEntity.entity))
        if (get${pageEntity.name}() != null) {
            // This entity is of type '${pageEntity.entity.id}'. There is a form on this page which 
            // updates an entity of type '${pageEntity.entity.id}'. Therefore, use this entity to prePopulate the inputs
#foreach ($input in $entityForm.entityInputs.get($pageEntity.entity))
#if ($input.entityProperty)
#if ($input.entityProperty.type == 'FILE')
            // set${entityForm.name}${input.name}(get${pageEntity.name}().get${input.entityProperty.name}().clone());
#else
            set${input.x.javaName}(get${pageEntity.name}().get${input.entityProperty.name}());
#end
#end ## if this input is bound to entity property
#end
        }
#end ## if any entities are updated
    }

    protected ${entity.name} load${pageEntity.name}() {
#if (${pageEntity.type} == 'PARAM_PRIMARY_KEY')
        return ${pageEntity.pageParameter.entityProperty.entity.id}Dao.findByPrimaryKey(${pageEntity.pageParameter.id});
#else
    return null;
#end
    }

    protected void reload${pageEntity.name}() {
        clear${pageEntity.name}();
        load${pageEntity.name}();
    }

    protected void clear${pageEntity.name}() {
        set${pageEntity.name}(null);
    }
#end ## // foreach

#foreach($pageParameter in $page.parameters)
#getterSetter({"type" : "String", "id" : "${pageParameter.id}", "name" : "${pageParameter.name}", "title" : "${pageParameter.title}", "description" : "${pageParameter.description}"})
#end
}